package edu.bbu.cshu.wmpc.checking.similarity;

import java.text.DecimalFormat;
import java.util.ArrayList;
import edu.bbu.cshu.wmpc.checking.Checking;
import edu.bbu.cshu.wmpc.core.LevelTypes;
import edu.bbu.cshu.wmpc.core.SourceCodeWmpc;
/**
 * <ul>
 * <li><b><i>Similarity</i></b></li>
 * <li style="list-style: none;"></li>
 * <li style="list-style: none;">
 * Abstract class that implements {@link Checking} and is to be extended by all similarity test performing classes.  
 * 
 * @author WMPC team
 * </li>
 * </ul>
 */ 
public abstract class Similarity implements Checking{
	/**
	 * <ul>
	 * <li><b><i>limit</i></b></li>
	 * <li style="list-style: none;"></li>
	 * <li style="list-style: none;">
	 * {@code double} value that specifies the limit at which the test considers two lines of the source code similar.  
	 * 
	 * </li>
	 * </ul>
	 */
	protected double limit;
	/**
	 * <ul>
	 * <li><b><i>special</i></b></li>
	 * <li style="list-style: none;"></li>
	 * <li style="list-style: none;">
	 * {@link String} that specifies a set of characters that are to be included in the similarity test.  
	 * 
	 * </li>
	 * </ul>
	 */
	protected String specia = "!=+-*/%<>&[]";
	/**
	 * <ul style="margin-top: 24px;">
	 * <li><b><i>setString</i></b></li>
	 * <li style="list-style: none;"></li>
	 * <li style="list-style: none;">
	 * <p style="margin-left: 7px;">{@code protected abstract void setString(int index, {@link String} {@code str)}</p>
	 * <br>	
	 * Sets the value of the first or second string, as specified by the {@code index}, to the value of the given {@code str}. Only {@code special} characters, letters and digits are left. One character long words are excluded. 
	 * 
	 * @param index - the index of the string that is to be set
	 * @param str	- the string that is to be set
	 * </li>
	 * </ul>
	 */
	protected abstract void setString(int index, String str);/**
	 * <ul style="margin-top: 24px;">
	 * <li><b><i>executeStringTest</i></b></li>
	 * <li style="list-style: none;"></li>
	 * <li style="list-style: none;">
	 * <p style="margin-left: 7px;">{@code protected abstract double executeStringTest()}</p>
	 * <br>	
	 * Performs a test that will indicate the percentage of similarity between the two preset {@code Strings}.  
	 * 
	 * @return a {@code double} value that indicates the percentage of similarity
	 * </li>
	 * </ul>
	 */
	protected abstract double executeStringTest();
	@Override
	public ArrayList<Object> executeTest(LevelTypes level, SourceCodeWmpc sc1, SourceCodeWmpc sc2) {
		ArrayList<String> f1 = null;
		ArrayList<String> f2 = null;
		ArrayList<Integer> similarRows = new ArrayList<Integer>();
		
		switch (level) {
			case ORIGINAL:
				f1 = sc1.getOriginal();
				f2 = sc2.getOriginal();
				break;
			case MODIFIED:
				f1 = sc1.getModified();
				f2 = sc2.getModified();
				break;
		}
		
		int count = 0;
		int noCount = 0;
		boolean suspicious;
		String tmp;
		
		for (int i = 0; i < f1.size(); i++) {
			if ((tmp = f1.get(i)).replaceAll("\\s", "").length() > 1) {
				if (!tmp.matches("##### .* #####")) {
					setString(1, tmp);
					
					suspicious = false;
					for (int j = 0; j < f2.size(); j++) {
						if ((tmp = f2.get(j)).replaceAll("\\s", "").length() > 1) {
							if (!tmp.matches("##### .* #####")) {
								setString(2, tmp);
								
								if((executeStringTest()*100) >= limit) {
									suspicious = true;
									switch (level) {
										case ORIGINAL:
											similarRows.add(i);
											similarRows.add(j);
											break;
										case MODIFIED: 
											similarRows.add(sc1.getValue(i));
											similarRows.add(sc2.getValue(j));
											break;
									}
								}
							}
						}
					}
					
					if (suspicious) {
						count++;
					}
				}
			} else {
				noCount++;
			}
		}

		double similarityPercent = (double) (count * 100) / (f1.size() - noCount);
		DecimalFormat twoDForm = new DecimalFormat("#.##");
		
		ArrayList<Object> results = new ArrayList<Object>();
		results.add(Double.valueOf(twoDForm.format(similarityPercent)));
		results.add(similarRows);
		return results;
	}
}
